package com.dayatang.exam.domain;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.dayatang.domain.AbstractEntity;
import com.dayatang.domain.QuerySettings;
import com.dayatang.utils.CollectionUtils;

/**
 * 考试�?
 * 
 * @author yang
 * 
 */
@Entity
@Table(name = "EXAMS")
public class Exam extends AbstractEntity {

	private static final long serialVersionUID = -6502946124854888482L;

	public static final int STATUS_CREATED = 1;

	public static final int STATUS_PUBLISHED = 2;

	public static final int STATUS_SCORED = 3;

	/**
	 * 考试名称
	 */
	@Column(nullable = false)
	private String name;

	/**
	 * �??时间
	 */
	@Temporal(TemporalType.TIMESTAMP)
	@Column(nullable = false)
	private Date fromTime;

	/**
	 * 结束时间
	 */
	@Temporal(TemporalType.TIMESTAMP)
	@Column(nullable = false)
	private Date toTime;

	/**
	 * 考试时长（分钟）
	 */
	@Column(nullable = false)
	private int minutes;

	/**
	 * 成绩公布日期
	 */
	@Column(name = "PUBLISH_SCORE_DATE")
	private Date publishScoreDate;

	/**
	 * 状态
	 */
	private int status;

	/**
	 * 及格分数
	 */
	private int passPoint;

	/**
	 * 可否自由报名
	 */
	private boolean allowRegister = false;

	/**
	 * 是否发布通知
	 */
	private boolean notify = false;

	/**
	 * 创建人
	 */
	@ManyToOne
	@JoinColumn(name = "CREATOR_ID", nullable = false)
	private User creator;

	/**
	 * 创建时间
	 */
	@Temporal(TemporalType.TIMESTAMP)
	private Date created;

	/**
	 * 考生相关信息
	 */
	@OneToMany(cascade = CascadeType.ALL, mappedBy = "exam", orphanRemoval = true)
	private Set<Registration> registrations = new HashSet<Registration>();

	/**
	 * 阅卷人
	 */
	@ManyToMany
	@JoinTable(name = "EXAM_VALUATORS", joinColumns = @JoinColumn(name = "EXAM_ID"), inverseJoinColumns = @JoinColumn(name = "USER_ID"))
	private Set<User> valuators = new HashSet<User>();

	/**
	 * 试题相关信息
	 */
	@ElementCollection
	@CollectionTable(name = "EXAM_QUESTIONS", joinColumns = @JoinColumn(name = "EXAM_ID"))
	private Set<ExamQuestionInfo> questionInfos = new HashSet<ExamQuestionInfo>();

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getFromTime() {
		return fromTime;
	}

	public void setFromTime(Date fromTime) {
		this.fromTime = fromTime;
	}

	public Date getToTime() {
		return toTime;
	}

	public void setToTime(Date toTime) {
		this.toTime = toTime;
	}

	public int getMinutes() {
		return minutes;
	}

	public void setMinutes(int minutes) {
		this.minutes = minutes;
	}

	public boolean isStarted() {
		return fromTime.before(new Date());
	}

	public boolean isEnded() {
		return toTime.before(new Date());
	}

	public boolean isPublished() {
		return publishScoreDate.before(new Date());
	}

	public Date getPublishScoreDate() {
		return publishScoreDate;
	}

	public void setPublishScoreDate(Date publishScoreDate) {
		this.publishScoreDate = publishScoreDate;
	}

	public int getStatus() {
		return status;
	}

	public void setStatus(int status) {
		this.status = status;
	}

	public int getPassPoint() {
		return passPoint;
	}

	public void setPassPoint(int passPoint) {
		this.passPoint = passPoint;
	}

	public boolean isNotify() {
		return notify;
	}

	public void setNotify(boolean notify) {
		this.notify = notify;
	}

	public boolean isAllowRegister() {
		return allowRegister;
	}

	public void setAllowRegister(boolean allowSignup) {
		this.allowRegister = allowSignup;
	}

	public User getCreator() {
		return creator;
	}

	public void setCreator(User creator) {
		this.creator = creator;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Set<Registration> getRegistrations() {
		return registrations;
	}

	public void setRegistrations(Set<Registration> registrations) {
		this.registrations = registrations;
	}
	
	public List<User> getStudents() {
		return Registration.findStudentsByExam(this);
	}
	
	/**
	 * 添加考生
	 * 
	 * @param user
	 *            要添加的考生
	 */
	public void registerStudent(User student) {
		if (containsStudent(student)) {
			return;
		}
		Registration registration = new Registration(this, student);
		registration.save();
		registrations.add(registration);
	}

	/**
	 * 剔除考生
	 * 
	 * @param user
	 */
	public void unregisterStudent(User student) {
		Registration registration = Registration.get(this, student);
		if (registration != null) {
			registration.cancel();
		}
		registrations.remove(registration);
	}

	/**
	 * 本次考试是否包含指定的考生
	 * 
	 * @param user
	 * @return
	 */
	public boolean containsStudent(User student) {
		return getStudents().contains(student);
	}
	
	public String getUserIds() {
		return CollectionUtils.join(getStudents(), "id", ",");
	}

	public String getUserNames() {
		return CollectionUtils.join(getStudents(), "name", ",");
	}

	public int getStudentCount() {
		return getStudents().size();
	}

	public Set<User> getValuators() {
		return valuators;
	}

	public void setValuators(Set<User> valuators) {
		this.valuators = valuators;
	}

	public String getValuatorsIds() {
		return CollectionUtils.join(getValuators(), "id", ",");
	}

	public String getValuatorsNames() {
		return CollectionUtils.join(getValuators(), "name", ",");
	}

	public Set<ExamQuestionInfo> getQuestionInfos() {
		return questionInfos;
	}

	public void setQuestionInfos(Set<ExamQuestionInfo> questionInfos) {
		this.questionInfos = questionInfos;
	}

	/**
	 * 获得指定题型的数量
	 * 
	 * @param questionType
	 * @return
	 */
	public int getQuestionCount(String questionType) {
		int result = 0;
		for (ExamQuestionInfo questionInfo : questionInfos) {
			if (questionInfo.getQuestionType().equals(questionType)) {
				result += questionInfo.getQuestionCount();
			}
		}
		return result;
	}

	/**
	 * 获得指定题型的�?分数
	 * 
	 * @param questionType
	 *            题目类型，如填空题，单项选择题等
	 * @return 指定题型的分数
	 */
	public int getPoint(String questionType) {
		int result = 0;
		for (ExamQuestionInfo questionInfo : questionInfos) {
			if (questionInfo.getQuestionType().equals(questionType)) {
				result += questionInfo.getPoint() * questionInfo.getQuestionCount();
			}
		}
		return result;
	}

	/**
	 * 获得考卷的总分数
	 * 
	 * @return 总分数
	 */
	public int getPoint() {
		int result = 0;
		for (ExamQuestionInfo questionInfo : questionInfos) {
			result += questionInfo.getPoint() * questionInfo.getQuestionCount();
		}
		return result;
	}

	/**
	 * 根据ID获取考试实例
	 * 
	 * @param id
	 * @return
	 */
	public static Exam get(Long id) {
		return getRepository().get(Exam.class, id);
	}

	/**
	 * 发布考试。�?生只能看到发布后的�?�?
	 */
	public void publish() {
		status = STATUS_PUBLISHED;
		save();
	}

	/**
	 * 考试完成。阅卷也完成了�?
	 */
	public void complete() {
		status = STATUS_SCORED;
		save();
	}

	/**
	 * 列出�?��的�?�?
	 * 
	 * @return
	 */
	public static Collection<Exam> list() {
		return getRepository().findAll(Exam.class);
	}

	/**
	 *列出正在进行的�?�?
	 */
	public static Collection<Exam> listUnderway() {
		Date now = new Date();
		return getRepository().find(QuerySettings.create(Exam.class)
				.le("fromTime", now)
				.gt("toTime", now));
	}

	/*
	public AnswerPaper createAnswerPaper(User user) throws QuestionInsufficientException {
		AnswerPaper result = new AnswerPaper(this, user);
		result.setStatus(AnswerPaper.STATUS_STARTED);
		result.setFromTime(Calendar.getInstance().getTime());
		result.setToTime(calculateToTime(result.getFromTime(), minutes));
		for (ExamQuestionInfo questionInfo : questionInfos) {
			result.addSolutions(questionInfo.generateSolutions());
		}
		result.save();
		return result;
	}
	*/

	private Date calculateToTime(Date start, int minutes) {
		Calendar end = Calendar.getInstance();
		end.setTime(start);
		end.add(Calendar.MINUTE, minutes);
		return end.getTime();
	}

	/**
	 * 列出可供指定用户报名的考试
	 * 
	 * @param user
	 * @return
	 */
	public static Collection<Exam> listUnsignuped(User user) {
		return examRepository.listUnsignuped(user);
	}

	/**
	 * 列出可供指定用户马上参加的�?�?
	 * 
	 * @param currentUser
	 * @return
	 */
	public static Collection<Exam> listSignuped(User user) {
		return examRepository.listSignuped(user);
	}

	/**
	 * 列出指定用户可以阅卷的�?�?
	 * 
	 * @param currentUser
	 * @return
	 */
	public static Collection<Exam> findByValuator(User user) {
		return examRepository.findByValuator(user);
	}

	/**
	 * 是否包含主观�?
	 * 
	 * @return
	 */
	public boolean hasSubjectiveQuestions() {
		return getQuestionCount(Question.TYPE_FILL_BLANK) + getQuestionCount(Question.TYPE_QUIZ) > 0;
	}

	/**
	 * 列出�?��已报名的考生
	 * 
	 * @return
	 */
	public Set<User> getSignupedStudents() {
		Set<User> result = new HashSet<User>();
		for (ExamStudentInfo studentInfo : studentInfos) {
			result.add(studentInfo.getUser());
		}
		return result;
	}

	/**
	 * 列出�?��参加了�?试的考生
	 * 
	 * @return
	 */
	public Set<User> getAttendedStudents() {
		Set<User> result = new HashSet<User>();
		for (AnswerPaper examPaper : getAnswerPapers()) {
			result.add(examPaper.getUser());
		}
		return result;
	}

	/**
	 * 列出�?��缺席考试的�?�?
	 * 
	 * @return
	 */
	public Set<User> getAbsentStudents() {
		Set<User> result = getSignupedStudents();
		result.removeAll(getAttendedStudents());
		return result;
	}

	/**
	 * 列出�?��已交卷的学生
	 * 
	 * @return
	 */
	public Set<User> getSubmittedStudents() {
		Set<User> result = new HashSet<User>();
		for (ExamPaper examPaper : ExamPaper.listSubmitted(this)) {
			result.add(examPaper.getUser());
		}
		return result;
	}

	/**
	 * 列出�?��正在考试的学�?
	 * 
	 * @return
	 */
	public Set<User> getUnderwayStudents() {
		Set<User> result = getAttendedStudents();
		result.removeAll(getSubmittedStudents());
		return result;
	}

	public int getLength() {
		return this.getPaperPath().length();
	}

	/**
	 * 查找考生信息
	 * 
	 * @param student
	 * @return
	 */
	public List<ExamStudentInfo> findStudentInfosByStudent(User student) {
		String name = student.getName();
		String idNumber = student.getIdNumber();
		String code = student.getCode();
		Long organizationId = student.getOrganization().getId();
		if (StringUtils.isEmpty(name) && StringUtils.isEmpty(idNumber) && StringUtils.isEmpty(code)
				&& (organizationId == null || organizationId.intValue() == 0)) {
			return getStudentInfos();
		}
		Set<ExamStudentInfo> result = new HashSet<ExamStudentInfo>(studentInfos);
		for (ExamStudentInfo studentInfo : studentInfos) {
			if (!StringUtils.isEmpty(name) 
					&& !studentInfo.getUser().getName().contains(name)) {
				result.remove(studentInfo);
			}
			if (!StringUtils.isEmpty(code) 
					&& !studentInfo.getUser().getCode().equalsIgnoreCase(code)) {
				result.remove(studentInfo);
			}
			if (!StringUtils.isEmpty(idNumber) 
					&& !studentInfo.getUser().getIdNumber().contains(idNumber)) {
				result.remove(studentInfo);
			}
			
			if (organizationId != null && organizationId.intValue() != 0 ) {
					//&& !studentInfo.getUser().getOrganization().getId().equals(organizationId)) {
				Organization organization = Organization.get(organizationId);
				if (!organization.containsStudent(studentInfo.getUser().getOrganization())) {
					result.remove(studentInfo);
				}
			}
		}
		return new ArrayList<ExamStudentInfo>(result);
	}

	@Override
	public boolean equals(final Object other) {
		if (!(other instanceof Exam))
			return false;
		Exam castOther = (Exam) other;
		return new EqualsBuilder().append(getId(), castOther.getId()).append(name, castOther.name).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(getId()).append(name).toHashCode();
	}

	@Override
	public String toString() {
		return new ToStringBuilder(this).append("name", name).append("fromTime", fromTime).append("toTime", toTime).toString();
	}
}
